-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom mapping and import settings for bank sync providers #4253
base: master
Are you sure you want to change the base?
Conversation
✅ Deploy Preview for actualbudget ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
Bundle Stats — desktop-clientHey there, this message comes from a GitHub action that helps you and reviewers to understand how these changes affect the size of this project's bundle. As this PR is updated, I'll keep you updated on how the bundle size is impacted. Total
Changeset
View detailed bundle breakdownAdded No assets were added Removed No assets were removed Bigger
Smaller
Unchanged
|
Bundle Stats — loot-coreHey there, this message comes from a GitHub action that helps you and reviewers to understand how these changes affect the size of this project's bundle. As this PR is updated, I'll keep you updated on how the bundle size is impacted. Total
Changeset
View detailed bundle breakdownAdded No assets were added Removed No assets were removed Bigger
Smaller No assets were smaller Unchanged No assets were unchanged |
f37d495
to
5118830
Compare
/update-vrt |
WalkthroughThe pull request introduces a comprehensive bank synchronization feature for the Actual Budget application. The changes span multiple packages, including Assessment against linked issues
Possibly related PRs
Suggested labels
Suggested reviewers
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (21)
packages/loot-core/src/server/accounts/sync.ts (4)
355-356
: Skipping non-cleared transactions
IfimportPending
is false, non-cleared transactions are skipped altogether. Consider how these pending transactions might be handled if the user later enables pending imports.
369-369
: Confirm the strict requirement for a 'date'
Throwing an error ifdate
is missing halts the entire sync. Consider skipping or partially applying such transactions instead of stopping the process.
377-377
: Ensure empty strings are replaced
Using|| payeeName
won't replacetrans.imported_payee
if it's an empty string. A trim-based fallback may help unify the logic.
389-389
: Assess storing raw synced data
raw_synced_data
may include personal or sensitive data. Consider encrypting it, partially redacting it, or limiting its size to avoid potential data exposure or excessive bloat.
[security]Also applies to: 394-394, 395-395, 400-400
packages/loot-core/src/types/models/account.d.ts (1)
21-21
: Consider using a typed date
Iflast_sync
is meant for date operations, storing it as a string may limit type-safety. Evaluate using an ISO datetime or a dedicated date type.packages/loot-core/src/types/models/transaction.d.ts (1)
35-35
: Simplify the type definition.The type definition can be simplified since the
undefined
type is redundant for optional properties.- raw_synced_data?: string | undefined; + raw_synced_data?: string;packages/loot-core/src/server/util/custom-sync-mapping.ts (1)
21-38
: Refactor default mappings to reduce duplication.The mappings for 'payment' and 'deposit' are identical. Consider extracting the common mappings to reduce duplication.
+const commonFieldMappings = new Map([ + ['date', 'date'], + ['payee', 'payeeName'], + ['notes', 'remittanceInformationUnstructured'], +]); + export const defaultMappings: Mappings = new Map([ - [ - 'payment', - new Map([ - ['date', 'date'], - ['payee', 'payeeName'], - ['notes', 'remittanceInformationUnstructured'], - ]), - ], - [ - 'deposit', - new Map([ - ['date', 'date'], - ['payee', 'payeeName'], - ['notes', 'remittanceInformationUnstructured'], - ]), - ], + ['payment', new Map(commonFieldMappings)], + ['deposit', new Map(commonFieldMappings)], ]);packages/desktop-client/src/components/banksync/AccountsHeader.tsx (2)
14-14
: Remove empty style object.The empty style object on TableHeader is unnecessary.
- <TableHeader style={{}}> + <TableHeader>
15-34
: Enhance accessibility and reduce style duplication.The table header cells need aria labels for better accessibility. Also, the padding style is duplicated across cells.
+const cellStyle = { paddingLeft: '10px' }; + <Cell value={t('Account')} width={!unlinked ? 250 : 'flex'} - style={{ paddingLeft: '10px' }} + style={cellStyle} + aria-label={t('Account column header')} /> {!unlinked && ( <> <Cell value={t('Bank')} width="flex" - style={{ paddingLeft: '10px' }} + style={cellStyle} + aria-label={t('Bank column header')} /> <Cell value={t('Last sync')} width={160} - style={{ paddingLeft: '10px' }} + style={cellStyle} + aria-label={t('Last sync column header')} /> - <Cell value="" width={100} style={{ paddingLeft: '10px' }} /> + <Cell value="" width={100} style={cellStyle} /> </> )}packages/desktop-client/src/components/banksync/AccountsList.tsx (2)
9-14
: Use literal union type for action.The action type can be more strictly typed using a literal union type.
+type AccountAction = 'link' | 'edit'; + type AccountsListProps = { accounts: AccountEntity[]; hoveredAccount?: string | undefined; onHover: (id: AccountEntity['id']) => void; - onAction: (account: AccountEntity, action: 'link' | 'edit') => void; + onAction: (account: AccountEntity, action: AccountAction) => void; };
28-40
: Optimize performance for large lists.For better performance with large account lists, consider memoizing the AccountRow components and callback functions.
+import React, { useCallback, useMemo } from 'react'; + +const MemoizedAccountRow = React.memo(AccountRow); + export function AccountsList({ accounts, hoveredAccount, onHover, onAction, }: AccountsListProps) { + const handleHover = useCallback(onHover, [onHover]); + const handleAction = useCallback(onAction, [onAction]); + if (accounts.length === 0) { return null; } + + const accountRows = useMemo( + () => + accounts.map(account => { + const hovered = hoveredAccount === account.id; + return ( + <MemoizedAccountRow + key={account.id} + account={account} + hovered={hovered} + onHover={handleHover} + onAction={handleAction} + /> + ); + }), + [accounts, hoveredAccount, handleHover, handleAction], + ); return ( <View> - {accounts.map(account => { - const hovered = hoveredAccount === account.id; - - return ( - <AccountRow - key={account.id} - account={account} - hovered={hovered} - onHover={onHover} - onAction={onAction} - /> - ); - })} + {accountRows} </View> );packages/loot-core/src/types/prefs.d.ts (1)
31-33
: LGTM! Consider adding JSDoc comments.The new preference types follow the existing pattern and naming conventions. To improve maintainability, consider adding JSDoc comments explaining the purpose of each preference type.
+ /** Custom field mappings for bank sync providers */ | `custom-sync-mappings-${string}` + /** Controls import of pending transactions per account */ | `sync-import-pending-${string}` + /** Controls import of transaction notes per account */ | `sync-import-notes-${string}`packages/desktop-client/src/components/banksync/AccountRow.tsx (2)
13-20
: Consider movingtsToString
to a shared utility module.This timestamp formatting utility could be useful in other components. Consider moving it to a shared utility module for reusability.
47-48
: Use optional chaining for event handlers.The static analysis tool correctly identifies potential null reference issues. Use optional chaining for cleaner code.
- onMouseEnter={() => onHover && onHover(account.id)} - onMouseLeave={() => onHover && onHover(null)} + onMouseEnter={() => onHover?.(account.id)} + onMouseLeave={() => onHover?.(null)}🧰 Tools
🪛 Biome (1.9.4)
[error] 47-47: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 48-48: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
packages/desktop-client/src/components/banksync/index.tsx (3)
24-34
: Consider making provider list more maintainable.The provider names are currently hardcoded in the hook. Consider moving these to a configuration file or deriving them from the BankSyncProviders type to make future additions easier.
+import { type BankSyncProviders } from 'loot-core/types/models'; + +const SYNC_PROVIDERS: Record<BankSyncProviders, string> = { + goCardless: 'GoCardless', + simpleFin: 'SimpleFIN', +}; + const useSyncSourceReadable = () => { const { t } = useTranslation(); const syncSourceReadable: Record<SyncProviders, string> = { - goCardless: 'GoCardless', - simpleFin: 'SimpleFIN', + ...SYNC_PROVIDERS, unlinked: t('Unlinked'), };
48-74
: Optimize memoization of grouped accounts.The
groupedAccounts
memo could be optimized by extracting the reducer function and memoizing it separately.+const useAccountGrouping = () => { + const groupAccounts = useCallback((accounts: AccountEntity[]) => { + return accounts + .filter(a => !a.closed) + .reduce( + (acc, a) => { + const syncSource = a.account_sync_source ?? 'unlinked'; + acc[syncSource] = acc[syncSource] || []; + acc[syncSource].push(a); + return acc; + }, + {} as Record<SyncProviders, AccountEntity[]>, + ); + }, []); + + return { groupAccounts }; +}; export function BankSync() { + const { groupAccounts } = useAccountGrouping(); const groupedAccounts = useMemo(() => { - const unsorted = accounts - .filter(a => !a.closed) - .reduce( - (acc, a) => { - const syncSource = a.account_sync_source ?? 'unlinked'; - acc[syncSource] = acc[syncSource] || []; - acc[syncSource].push(a); - return acc; - }, - {} as Record<SyncProviders, AccountEntity[]>, - ); + const unsorted = groupAccounts(accounts);
93-95
: Memoize the onHover callback.The
onHover
callback should be memoized to prevent unnecessary re-renders of child components.- const onHover = useCallback((id: AccountEntity['id']) => { + const onHover = useCallback((id: AccountEntity['id']) => { setHoveredAccount(id); - }, []); + }, [setHoveredAccount]);packages/desktop-client/src/components/banksync/FieldMapping.tsx (1)
16-31
: Extract transaction direction options to constants.Consider moving the transaction direction options to a separate constants file for better maintainability.
+const TRANSACTION_DIRECTIONS = { + PAYMENT: 'payment', + DEPOSIT: 'deposit', +} as const; + +type TransactionDirectionType = typeof TRANSACTION_DIRECTIONS[keyof typeof TRANSACTION_DIRECTIONS]; + const useTransactionDirectionOptions = () => { const { t } = useTranslation(); const transactionDirectionOptions = [ { - value: 'payment', + value: TRANSACTION_DIRECTIONS.PAYMENT, label: t('Payment'), }, { - value: 'deposit', + value: TRANSACTION_DIRECTIONS.DEPOSIT, label: t('Deposit'), }, ];packages/desktop-client/src/components/banksync/EditSyncAccount.tsx (2)
42-66
: Extract mappable fields configuration.Consider moving the mappable fields configuration to a separate file for better maintainability and reusability.
+// mappableFieldsConfig.ts +export const MAPPABLE_FIELDS_CONFIG: MappableField[] = [ + { + actualField: 'date', + syncFields: ['date', 'bookingDate', 'valueDate'], + }, + // ... other fields +]; -const mappableFields: MappableField[] = [ - { - actualField: 'date', - syncFields: ['date', 'bookingDate', 'valueDate'], - }, - // ... rest of the fields -]; +import { MAPPABLE_FIELDS_CONFIG } from './mappableFieldsConfig'; + +const mappableFields = MAPPABLE_FIELDS_CONFIG;
86-93
: Extract preference management to custom hook.Consider creating a custom hook to manage related preferences, improving code organization and reusability.
+const useAccountPreferences = (accountId: string) => { + const [savedMappings, setSavedMappings] = useSyncedPref( + `custom-sync-mappings-${accountId}`, + mappingsToString(defaultMappings) + ); + const [savedImportNotes, setSavedImportNotes] = useSyncedPref( + `sync-import-notes-${accountId}`, + true + ); + const [savedImportPending, setSavedImportPending] = useSyncedPref( + `sync-import-pending-${accountId}`, + true + ); + + return { + savedMappings, + setSavedMappings, + savedImportNotes, + setSavedImportNotes, + savedImportPending, + setSavedImportPending, + }; +};packages/loot-core/src/server/main.ts (1)
1064-1066
: Consider using ISO string for better timestamp readability.While storing milliseconds since epoch works, using an ISO string format would make the timestamps more readable in the database while maintaining sortability.
-const ts = new Date().getTime().toString(); +const ts = new Date().toISOString();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (26)
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-checks-the-page-visuals-1-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-checks-the-page-visuals-2-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-checks-the-page-visuals-3-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-1-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-2-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-3-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-4-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-5-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-6-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-split-transaction-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-1-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-split-transaction-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-2-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-split-transaction-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-3-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-10-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-11-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-12-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-13-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-14-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-15-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/settings.test.ts-snapshots/Settings-checks-the-page-visuals-1-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/settings.test.ts-snapshots/Settings-checks-the-page-visuals-2-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/settings.test.ts-snapshots/Settings-checks-the-page-visuals-3-chromium-linux.png
is excluded by!**/*.png
packages/loot-core/src/server/__snapshots__/main.test.ts.snap
is excluded by!**/*.snap
packages/loot-core/src/server/accounts/__snapshots__/parse-file.test.ts.snap
is excluded by!**/*.snap
packages/loot-core/src/server/accounts/__snapshots__/sync.test.ts.snap
is excluded by!**/*.snap
packages/loot-core/src/server/accounts/__snapshots__/transfer.test.ts.snap
is excluded by!**/*.snap
upcoming-release-notes/4253.md
is excluded by!**/*.md
📒 Files selected for processing (22)
packages/desktop-client/src/components/FinancesApp.tsx
(2 hunks)packages/desktop-client/src/components/Modals.tsx
(2 hunks)packages/desktop-client/src/components/banksync/AccountRow.tsx
(1 hunks)packages/desktop-client/src/components/banksync/AccountsHeader.tsx
(1 hunks)packages/desktop-client/src/components/banksync/AccountsList.tsx
(1 hunks)packages/desktop-client/src/components/banksync/EditSyncAccount.tsx
(1 hunks)packages/desktop-client/src/components/banksync/FieldMapping.tsx
(1 hunks)packages/desktop-client/src/components/banksync/index.tsx
(1 hunks)packages/desktop-client/src/components/common/Input.tsx
(1 hunks)packages/desktop-client/src/components/sidebar/PrimaryButtons.tsx
(3 hunks)packages/loot-core/migrations/1738072673000_bank_sync_page.sql
(1 hunks)packages/loot-core/src/client/accounts/accountsSlice.ts
(1 hunks)packages/loot-core/src/client/state-types/modals.d.ts
(1 hunks)packages/loot-core/src/mocks/index.ts
(3 hunks)packages/loot-core/src/server/accounts/sync.ts
(3 hunks)packages/loot-core/src/server/aql/schema/index.ts
(2 hunks)packages/loot-core/src/server/main.ts
(4 hunks)packages/loot-core/src/server/util/custom-sync-mapping.ts
(1 hunks)packages/loot-core/src/types/models/account.d.ts
(1 hunks)packages/loot-core/src/types/models/bank-sync.d.ts
(1 hunks)packages/loot-core/src/types/models/transaction.d.ts
(1 hunks)packages/loot-core/src/types/prefs.d.ts
(1 hunks)
🧰 Additional context used
📓 Learnings (6)
packages/loot-core/src/types/models/bank-sync.d.ts (1)
Learnt from: matt-fidd
PR: actualbudget/actual#3581
File: packages/loot-core/src/types/models/bank-sync.d.ts:11-21
Timestamp: 2024-11-10T16:45:25.627Z
Learning: In `packages/loot-core/src/types/models/bank-sync.d.ts`, when defining TypeScript types for data received from the server, maintain the field names as they are in the server response, even if they don't follow TypeScript naming conventions.
packages/loot-core/src/types/models/account.d.ts (1)
Learnt from: matt-fidd
PR: actualbudget/actual#3581
File: packages/loot-core/src/types/models/bank-sync.d.ts:11-21
Timestamp: 2024-11-10T16:45:25.627Z
Learning: In `packages/loot-core/src/types/models/bank-sync.d.ts`, when defining TypeScript types for data received from the server, maintain the field names as they are in the server response, even if they don't follow TypeScript naming conventions.
packages/loot-core/src/server/aql/schema/index.ts (1)
Learnt from: matt-fidd
PR: actualbudget/actual#4141
File: packages/loot-core/src/server/aql/schema/index.ts:145-145
Timestamp: 2025-01-12T20:26:15.249Z
Learning: In the schema definitions in `packages/loot-core/src/server/aql/schema/index.ts`, field definitions should follow the existing pattern using only the common properties (`type`, `default`, `required`, `ref`) for consistency.
packages/desktop-client/src/components/banksync/AccountsList.tsx (1)
Learnt from: joel-jeremy
PR: actualbudget/actual#3685
File: packages/desktop-client/src/components/accounts/Account.tsx:655-665
Timestamp: 2024-11-10T16:45:31.225Z
Learning: The Account component in 'packages/desktop-client/src/components/accounts/Account.tsx' is being rewritten in a separate PR.
packages/desktop-client/src/components/banksync/AccountsHeader.tsx (1)
Learnt from: joel-jeremy
PR: actualbudget/actual#3685
File: packages/desktop-client/src/components/accounts/Account.tsx:655-665
Timestamp: 2024-11-10T16:45:31.225Z
Learning: The Account component in 'packages/desktop-client/src/components/accounts/Account.tsx' is being rewritten in a separate PR.
packages/desktop-client/src/components/banksync/AccountRow.tsx (1)
Learnt from: joel-jeremy
PR: actualbudget/actual#3685
File: packages/desktop-client/src/components/accounts/Account.tsx:655-665
Timestamp: 2024-11-10T16:45:31.225Z
Learning: The Account component in 'packages/desktop-client/src/components/accounts/Account.tsx' is being rewritten in a separate PR.
🪛 Biome (1.9.4)
packages/desktop-client/src/components/banksync/AccountRow.tsx
[error] 47-47: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 48-48: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
🔇 Additional comments (25)
packages/loot-core/src/server/accounts/sync.ts (8)
29-32
: Looks good
These new imports for custom-sync-mapping appear clean and align with the usage innormalizeBankSyncTransactions
.
329-346
: Consider stricter validation of preferences for safer defaults
Currently, if the retrieved value is unexpected or absent, the code defaults to'true'
. Make sure that's the intended behavior for every scenario, or handle invalid strings more robustly.
347-350
: Handle malformed mapping strings
IfcustomMappingsRaw
is corrupted,mappingsFromString
may fail or yield inconsistent mappings. Consider adding error handling or a safer fallback todefaultMappings
.
353-354
: Confirm alignment with user expectations for 'cleared'
Marking every transaction as cleared ifbooked
is truthy might override any manually set states. Double-check that this is the desired logic.
361-361
: Check for zero-amount edge case
Here, zero-amount transactions are treated as deposits. Verify that the mapping keys for'deposit'
and'payment'
exist and make sense for such edge cases.
362-366
: Validate dynamic mapping fields
Relying onmapping.get('date')
, etc., is flexible, but ensure the user-specified mapping keys exist. Otherwise, critical fields (like the date) could be undefined.
373-373
: Consider a fallback payee
Some transactions may legitimately lack a payee. IfpayeeName
is null, consider using a placeholder or skipping them to avoid blocking the sync.
386-386
: Resolve payees concurrency
This runs within a loop and relies onpayeesToCreate
to avoid duplicates. Confirm no race conditions occur in parallel code or multi-transaction syncs.packages/loot-core/src/types/models/bank-sync.d.ts (1)
23-23
: Foresee extensibility
DefiningBankSyncProviders
as a union is straightforward; just remember to extend it if more providers are introduced.packages/loot-core/src/types/models/account.d.ts (1)
13-14
: Carefully handle null bank fields
WhenT
is false,bankName
andbankId
both become null. Verify that the UI and DB logic gracefully handle those null cases.packages/desktop-client/src/components/sidebar/PrimaryButtons.tsx (1)
9-9
: LGTM! Bank Sync navigation is well integrated.The Bank Sync menu item is appropriately placed in the sidebar navigation, with a suitable icon choice and correct route configuration.
Also applies to: 30-30, 67-72
packages/desktop-client/src/components/common/Input.tsx (1)
57-59
: LGTM! Good visual feedback for disabled state.The conditional color styling provides clear visual feedback when inputs are disabled, using appropriate theme variables.
packages/desktop-client/src/components/banksync/AccountRow.tsx (1)
22-27
: LGTM! Props interface is well-defined.The TypeScript interface for props is clear and type-safe, using appropriate types from the core package.
packages/desktop-client/src/components/banksync/index.tsx (1)
1-23
: LGTM! Well-organized imports and type definitions.The imports are properly structured, and the type definitions are clear and purposeful.
packages/loot-core/src/mocks/index.ts (1)
54-62
: LGTM! Consistent null initialization.The emptySyncFields function properly initializes all sync-related fields to null.
packages/loot-core/src/client/accounts/accountsSlice.ts (1)
193-194
: LGTM! Ensures account data is refreshed after sync.The addition of
dispatch(getAccounts());
improves data consistency by refreshing account data after handling sync responses.packages/loot-core/src/client/state-types/modals.d.ts (1)
189-193
: LGTM! Well-defined modal type for editing synced accounts.The new modal type is properly integrated and follows TypeScript best practices.
packages/desktop-client/src/components/FinancesApp.tsx (1)
27-27
: LGTM! Clean integration of bank sync feature.The bank sync feature is properly integrated with a new route and follows React Router patterns.
Also applies to: 252-252
packages/loot-core/src/server/aql/schema/index.ts (1)
56-56
: LGTM! Schema fields follow consistent pattern.The new fields
raw_synced_data
andlast_sync
are properly defined following the existing schema pattern, aligning with the established conventions.Also applies to: 77-77
packages/desktop-client/src/components/Modals.tsx (2)
14-14
: LGTM!The import statement follows the project's import style and is used in the new modal case.
388-390
: LGTM!The new modal case follows the established pattern, includes proper React key, and correctly passes the account prop.
packages/loot-core/src/server/main.ts (1)
Line range hint
1048-1063
: LGTM!The function is correctly marked as async to support awaiting database operations while maintaining the same interface.
packages/loot-core/migrations/1738072673000_bank_sync_page.sql (3)
1-2
: LGTM!The migration is properly wrapped in a transaction to ensure atomicity of the schema changes.
Also applies to: 7-7
3-3
: LGTM!The
last_sync
column is appropriately added with text type and no default value.
5-5
: LGTM!The
raw_synced_data
column is appropriately added with text type and no default value.
packages/desktop-client/src/components/banksync/FieldMapping.tsx
Outdated
Show resolved
Hide resolved
packages/desktop-client/src/components/banksync/EditSyncAccount.tsx
Outdated
Show resolved
Hide resolved
ec59e51
to
3dc37f1
Compare
@coderabbitai full review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (20)
packages/desktop-client/src/components/banksync/index.tsx (2)
1-23
: Consider code splitting or lazy-loading for improved performance.The comprehensive import list is fine, but if the bundle grows significantly, you may consider code-splitting or lazy-loading less frequently used components, especially for modals or config data, to optimize performance.
24-34
: Localize all sync provider strings.While "Unlinked" is localized via
t(...)
, strings like "GoCardless" and "SimpleFIN" are currently hardcoded. Consider wrapping them int(...)
for consistent localization support.- goCardless: 'GoCardless', - simpleFin: 'SimpleFIN', + goCardless: t('GoCardless'), + simpleFin: t('SimpleFIN'),packages/desktop-client/src/components/banksync/AccountsHeader.tsx (2)
14-14
: Remove unnecessary empty style object.The empty style object
style={{}}
onTableHeader
serves no purpose and can be removed.- <TableHeader style={{}}> + <TableHeader>
15-33
: Consider extracting hardcoded values to constants.The component has multiple instances of hardcoded padding and width values. Consider extracting these to theme constants or a configuration object for better maintainability and consistency.
+const CELL_STYLES = { + padding: { paddingLeft: '10px' }, + widths: { + account: 250, + lastSync: 160, + actions: 100, + }, +} as const; export function AccountsHeader({ unlinked }: AccountsHeaderProps) { const { t } = useTranslation(); return ( <TableHeader> <Cell value={t('Account')} - width={!unlinked ? 250 : 'flex'} + width={!unlinked ? CELL_STYLES.widths.account : 'flex'} - style={{ paddingLeft: '10px' }} + style={CELL_STYLES.padding} /> {!unlinked && ( <> <Cell value={t('Bank')} width="flex" - style={{ paddingLeft: '10px' }} + style={CELL_STYLES.padding} /> <Cell value={t('Last sync')} - width={160} - style={{ paddingLeft: '10px' }} + width={CELL_STYLES.widths.lastSync} + style={CELL_STYLES.padding} /> - <Cell value="" width={100} style={{ paddingLeft: '10px' }} /> + <Cell value="" width={CELL_STYLES.widths.actions} style={CELL_STYLES.padding} /> </> )} </TableHeader> ); }packages/desktop-client/src/components/banksync/AccountsList.tsx (1)
16-43
: Consider memoizing callbacks and mapped components for performance.While the implementation is clean, consider these performance optimizations:
- Memoize the
onHover
andonAction
callbacks usinguseCallback
- Memoize the mapped
AccountRow
components usinguseMemo
+import React, { useCallback, useMemo } from 'react'; export function AccountsList({ accounts, hoveredAccount, onHover, onAction, }: AccountsListProps) { if (accounts.length === 0) { return null; } + const memoizedOnHover = useCallback(onHover, [onHover]); + const memoizedOnAction = useCallback(onAction, [onAction]); + + const accountRows = useMemo( + () => + accounts.map(account => { + const hovered = hoveredAccount === account.id; + + return ( + <AccountRow + key={account.id} + account={account} + hovered={hovered} + onHover={memoizedOnHover} + onAction={memoizedOnAction} + /> + ); + }), + [accounts, hoveredAccount, memoizedOnHover, memoizedOnAction] + ); return ( <View> - {accounts.map(account => { - const hovered = hoveredAccount === account.id; - - return ( - <AccountRow - key={account.id} - account={account} - hovered={hovered} - onHover={onHover} - onAction={onAction} - /> - ); - })} + {accountRows} </View> ); }packages/loot-core/src/server/util/custom-sync-mapping.ts (2)
1-29
: Enhance type safety and error handling.While the implementation is good, consider these improvements:
- Add type guards for better type safety
- Add validation for the inner map values
- Make error messages more specific
+type JsonMapping = Record<string, Record<string, string>>; + +function isValidInnerMapping(value: unknown): value is Record<string, string> { + return ( + typeof value === 'object' && + value !== null && + Object.entries(value).every( + ([k, v]) => typeof k === 'string' && typeof v === 'string' + ) + ); +} + export const mappingsFromString = (str: string): Mappings => { try { const parsed = JSON.parse(str); if (typeof parsed !== 'object' || parsed === null) { - throw new Error('Invalid mapping format'); + throw new Error('Mapping must be a JSON object'); } + + for (const [key, value] of Object.entries(parsed)) { + if (!isValidInnerMapping(value)) { + throw new Error( + `Invalid inner mapping for key "${key}": all values must be strings` + ); + } + } + return new Map( - Object.entries(parsed).map(([key, value]) => [ + Object.entries(parsed as JsonMapping).map(([key, value]) => [ key, new Map(Object.entries(value as object)), ]), ); } catch (e) { const message = e instanceof Error ? e.message : e; - throw new Error(`Failed to parse mapping: ${message}`); + throw new Error( + e instanceof SyntaxError + ? 'Invalid JSON format in mapping string' + : `Failed to parse mapping: ${message}` + ); } };
31-48
: Reduce duplication in default mappings.The mappings for 'payment' and 'deposit' are identical. Consider extracting the common mapping to reduce duplication.
+const DEFAULT_FIELD_MAPPING = new Map([ + ['date', 'date'], + ['payee', 'payeeName'], + ['notes', 'remittanceInformationUnstructured'], +]); + export const defaultMappings: Mappings = new Map([ - [ - 'payment', - new Map([ - ['date', 'date'], - ['payee', 'payeeName'], - ['notes', 'remittanceInformationUnstructured'], - ]), - ], - [ - 'deposit', - new Map([ - ['date', 'date'], - ['payee', 'payeeName'], - ['notes', 'remittanceInformationUnstructured'], - ]), - ], + ['payment', new Map(DEFAULT_FIELD_MAPPING)], + ['deposit', new Map(DEFAULT_FIELD_MAPPING)], ]);packages/desktop-client/src/components/banksync/AccountRow.tsx (3)
13-18
: Consider moving tsToString to a shared utility.The
tsToString
function could be useful in other components that need to format timestamps. Consider moving it to a shared utility file.
45-46
: Use optional chaining for event handlers.The
onHover
callback checks could be simplified using optional chaining.- onMouseEnter={() => onHover && onHover(account.id)} - onMouseLeave={() => onHover && onHover(null)} + onMouseEnter={() => onHover?.(account.id)} + onMouseLeave={() => onHover?.(null)}🧰 Tools
🪛 Biome (1.9.4)
[error] 45-45: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 46-46: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
75-87
: Consider extracting button cell to a separate component.The conditional rendering of edit/link buttons could be extracted to a separate component to improve readability and reusability.
type ActionButtonProps = { account: AccountEntity; onAction: AccountRowProps['onAction']; }; const ActionButton: React.FC<ActionButtonProps> = ({ account, onAction }) => { if (account.account_sync_source) { return ( <Cell name="edit" plain style={{ paddingRight: '10px' }}> <Button onPress={() => onAction(account, 'edit')}> <Trans>Edit</Trans> </Button> </Cell> ); } return ( <Cell name="link" plain style={{ paddingRight: '10px' }}> <Button onPress={() => onAction(account, 'link')}> <Trans>Link account</Trans> </Button> </Cell> ); };packages/desktop-client/src/components/banksync/FieldMapping.tsx (2)
16-31
: Consider moving hook to a separate file.The
useTransactionDirectionOptions
hook could be moved to a separate hooks file for better organization and reusability.
85-152
: Extract field row to a separate component.The field mapping row rendering logic is complex and could be extracted to a separate component for better maintainability.
type FieldRowProps = { field: MappableFieldWithExample; mapping: Map<string, string>; setMapping: (field: string, value: string) => void; }; const FieldRow: React.FC<FieldRowProps> = ({ field, mapping, setMapping }) => { const { t } = useTranslation(); return ( <Row key={field.actualField} style={{ fontSize: 13, backgroundColor: theme.tableRowBackgroundHover, display: 'flex', alignItems: 'center', border: '1px solid var(--color-tableBorder)', minHeight: '40px', }} collapsed={true} > {/* ... rest of the row content ... */} </Row> ); };packages/desktop-client/src/components/banksync/EditSyncAccount.tsx (2)
42-66
: Add documentation for sync fields.Consider adding JSDoc comments to explain the purpose and expected format of each sync field, especially for fields like
remittanceInformationUnstructured
andremittanceInformationStructured
.
135-141
: Add loading state for preference updates.The
onSave
function updates multiple preferences but doesn't handle the loading state or potential failures.Consider this improvement:
const onSave = async (close: () => void) => { + try { + setIsSaving(true); const mappingsStr = mappingsToString(mappings); - setSavedMappings(mappingsStr); - setSavedImportPending(String(importPending)); - setSavedImportNotes(String(importNotes)); + await Promise.all([ + setSavedMappings(mappingsStr), + setSavedImportPending(String(importPending)), + setSavedImportNotes(String(importNotes)) + ]); close(); + } catch (error) { + console.error('Failed to save preferences:', error); + // Show error notification to user + } finally { + setIsSaving(false); + } };packages/loot-core/src/server/aql/schema/index.ts (1)
56-57
: Add documentation for new schema fields.Consider adding comments to explain:
- The purpose and format of
raw_synced_data
in transactions- The format and timezone of
last_sync
in accountsAlso applies to: 77-77
packages/loot-core/migrations/1738072673000_bank_sync_page.sql (1)
1-7
: Consider enhancing the schema with constraints and indexes.While the migration is functionally correct, consider these improvements:
- Add a default value (NULL) for new columns
- Add an index on
last_sync
if it will be frequently queried- Consider adding CHECK constraints if there are valid format requirements for these fields
BEGIN TRANSACTION; -ALTER TABLE accounts ADD COLUMN last_sync text; +ALTER TABLE accounts ADD COLUMN last_sync text DEFAULT NULL; +CREATE INDEX idx_accounts_last_sync ON accounts(last_sync); -ALTER TABLE transactions ADD COLUMN raw_synced_data text; +ALTER TABLE transactions ADD COLUMN raw_synced_data text DEFAULT NULL; COMMIT;packages/loot-core/src/types/models/account.d.ts (1)
13-14
: Maintain consistent field naming convention.The field naming is inconsistent:
bankName
andbankId
use camelCaselast_sync
uses snake_caseBased on the retrieved learning, we should maintain consistency with server field names. Additionally, consider adding JSDoc comments to document the purpose of these fields.
- bankName: T extends true ? string : null; - bankId: T extends true ? number : null; + /** Name of the bank as provided by the sync provider */ + bank_name: T extends true ? string : null; + /** Unique identifier for the bank in the provider's system */ + bank_id: T extends true ? number : null; mask: T extends true ? string : null; // end of bank account number official_name: T extends true ? string : null; balance_current: T extends true ? number : null; balance_available: T extends true ? number : null; balance_limit: T extends true ? number : null; account_sync_source: T extends true ? AccountSyncSource : null; + /** Timestamp of the last successful sync operation */ last_sync: T extends true ? string : null;Also applies to: 21-21
packages/loot-core/src/client/accounts/accountsSlice.ts (1)
193-194
: Consider optimizing the account refresh.While refreshing accounts after sync is necessary, consider debouncing multiple sync responses to prevent unnecessary re-renders when batch syncing accounts.
+import { debounce } from '../../shared/util'; + +const debouncedGetAccounts = debounce((dispatch) => { + dispatch(getAccounts()); +}, 1000); + function handleSyncResponse( accountId: AccountEntity['id'], res: SyncResponse, dispatch: AppDispatch, resNewTransactions: Array<TransactionEntity['id']>, resMatchedTransactions: Array<TransactionEntity['id']>, resUpdatedAccounts: Array<AccountEntity['id']>, ) { // ... existing code ... - dispatch(getAccounts()); + debouncedGetAccounts(dispatch);packages/loot-core/src/server/accounts/sync.ts (2)
353-355
: Consider adding a preference for cleared status.Currently, the code hardcodes the cleared status based on
booked
. Consider making this configurable likeimportPending
andimportNotes
.
394-395
: Sanitize notes before storage.The code replaces '#' with '##' but should consider other potential markdown characters that might need escaping.
- notes: importNotes ? notes.trim().replace('#', '##') : null, + notes: importNotes && notes ? sanitizeMarkdown(notes.trim()) : null,Consider adding a utility function:
function sanitizeMarkdown(text: string): string { return text .replace(/#/g, '##') .replace(/\*/g, '\\*') .replace(/_/g, '\\_'); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (26)
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-checks-the-page-visuals-1-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-checks-the-page-visuals-2-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-checks-the-page-visuals-3-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-1-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-2-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-3-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-4-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-5-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-6-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-split-transaction-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-1-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-split-transaction-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-2-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-split-transaction-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-3-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-10-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-11-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-12-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-13-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-14-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-15-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/settings.test.ts-snapshots/Settings-checks-the-page-visuals-1-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/settings.test.ts-snapshots/Settings-checks-the-page-visuals-2-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/settings.test.ts-snapshots/Settings-checks-the-page-visuals-3-chromium-linux.png
is excluded by!**/*.png
packages/loot-core/src/server/__snapshots__/main.test.ts.snap
is excluded by!**/*.snap
packages/loot-core/src/server/accounts/__snapshots__/parse-file.test.ts.snap
is excluded by!**/*.snap
packages/loot-core/src/server/accounts/__snapshots__/sync.test.ts.snap
is excluded by!**/*.snap
packages/loot-core/src/server/accounts/__snapshots__/transfer.test.ts.snap
is excluded by!**/*.snap
upcoming-release-notes/4253.md
is excluded by!**/*.md
📒 Files selected for processing (22)
packages/desktop-client/src/components/FinancesApp.tsx
(2 hunks)packages/desktop-client/src/components/Modals.tsx
(2 hunks)packages/desktop-client/src/components/banksync/AccountRow.tsx
(1 hunks)packages/desktop-client/src/components/banksync/AccountsHeader.tsx
(1 hunks)packages/desktop-client/src/components/banksync/AccountsList.tsx
(1 hunks)packages/desktop-client/src/components/banksync/EditSyncAccount.tsx
(1 hunks)packages/desktop-client/src/components/banksync/FieldMapping.tsx
(1 hunks)packages/desktop-client/src/components/banksync/index.tsx
(1 hunks)packages/desktop-client/src/components/common/Input.tsx
(1 hunks)packages/desktop-client/src/components/sidebar/PrimaryButtons.tsx
(3 hunks)packages/loot-core/migrations/1738072673000_bank_sync_page.sql
(1 hunks)packages/loot-core/src/client/accounts/accountsSlice.ts
(1 hunks)packages/loot-core/src/client/state-types/modals.d.ts
(1 hunks)packages/loot-core/src/mocks/index.ts
(3 hunks)packages/loot-core/src/server/accounts/sync.ts
(3 hunks)packages/loot-core/src/server/aql/schema/index.ts
(2 hunks)packages/loot-core/src/server/main.ts
(4 hunks)packages/loot-core/src/server/util/custom-sync-mapping.ts
(1 hunks)packages/loot-core/src/types/models/account.d.ts
(1 hunks)packages/loot-core/src/types/models/bank-sync.d.ts
(1 hunks)packages/loot-core/src/types/models/transaction.d.ts
(1 hunks)packages/loot-core/src/types/prefs.d.ts
(1 hunks)
🧰 Additional context used
📓 Learnings (6)
packages/loot-core/src/types/models/account.d.ts (1)
Learnt from: matt-fidd
PR: actualbudget/actual#3581
File: packages/loot-core/src/types/models/bank-sync.d.ts:11-21
Timestamp: 2024-11-10T16:45:25.627Z
Learning: In `packages/loot-core/src/types/models/bank-sync.d.ts`, when defining TypeScript types for data received from the server, maintain the field names as they are in the server response, even if they don't follow TypeScript naming conventions.
packages/loot-core/src/types/prefs.d.ts (1)
Learnt from: MatissJanis
PR: actualbudget/actual#3458
File: packages/loot-core/src/client/state-types/prefs.d.ts:5-5
Timestamp: 2024-11-10T16:45:25.627Z
Learning: In future reviews, ensure that changes related to `PrefsState` in `prefs.d.ts` do not incorrectly assume necessary updates in other parts of the codebase. Verify the impact thoroughly before making suggestions.
packages/desktop-client/src/components/banksync/AccountRow.tsx (2)
Learnt from: matt-fidd
PR: actualbudget/actual#4253
File: packages/desktop-client/src/components/banksync/AccountRow.tsx:0-0
Timestamp: 2025-01-28T23:24:32.957Z
Learning: In the Actual codebase, timestamp formatting should use date-fns' `format` function with explicit format strings (e.g., 'HH:mm:ss' for time) instead of JavaScript's built-in `toLocaleTimeString()` for consistency.
Learnt from: joel-jeremy
PR: actualbudget/actual#3685
File: packages/desktop-client/src/components/accounts/Account.tsx:655-665
Timestamp: 2024-11-10T16:45:31.225Z
Learning: The Account component in 'packages/desktop-client/src/components/accounts/Account.tsx' is being rewritten in a separate PR.
packages/desktop-client/src/components/banksync/AccountsList.tsx (1)
Learnt from: joel-jeremy
PR: actualbudget/actual#3685
File: packages/desktop-client/src/components/accounts/Account.tsx:655-665
Timestamp: 2024-11-10T16:45:31.225Z
Learning: The Account component in 'packages/desktop-client/src/components/accounts/Account.tsx' is being rewritten in a separate PR.
packages/desktop-client/src/components/banksync/AccountsHeader.tsx (1)
Learnt from: joel-jeremy
PR: actualbudget/actual#3685
File: packages/desktop-client/src/components/accounts/Account.tsx:655-665
Timestamp: 2024-11-10T16:45:31.225Z
Learning: The Account component in 'packages/desktop-client/src/components/accounts/Account.tsx' is being rewritten in a separate PR.
packages/loot-core/src/types/models/bank-sync.d.ts (1)
Learnt from: matt-fidd
PR: actualbudget/actual#3581
File: packages/loot-core/src/types/models/bank-sync.d.ts:11-21
Timestamp: 2024-11-10T16:45:25.627Z
Learning: In `packages/loot-core/src/types/models/bank-sync.d.ts`, when defining TypeScript types for data received from the server, maintain the field names as they are in the server response, even if they don't follow TypeScript naming conventions.
🪛 Biome (1.9.4)
packages/desktop-client/src/components/banksync/AccountRow.tsx
[error] 45-45: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 46-46: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
🔇 Additional comments (20)
packages/loot-core/src/server/main.ts (3)
1048-1048
: Adopt consistent async patterns.Changing
handleSyncResponse
to an async function ensures you canawait
database updates. Ensure that all callers also leverageawait
or handle promise rejections as needed to avoid unhandled promise rejections.
1063-1066
: Validate the last sync timestamp value.You’re converting a timestamp to string before persisting. If future logic depends on strict numeric fields, consider storing numeric timestamps instead or clarifying in docs that
last_sync
is a text field.
1203-1203
: Await handleSyncResponse in batch flows.Now that
handleSyncResponse
is async, awaiting each handle ensures transactions and timestamps are updated before proceeding to subsequent operations. This is good for data consistency.packages/desktop-client/src/components/banksync/AccountsHeader.tsx (1)
1-8
: LGTM! Clean imports and type definition.The imports are well-organized, and the type definition is clear and concise.
packages/desktop-client/src/components/banksync/AccountsList.tsx (1)
1-14
: LGTM! Well-structured types and imports.The type definitions are clear and follow TypeScript best practices. The action type is properly constrained using a union type.
packages/desktop-client/src/components/sidebar/PrimaryButtons.tsx (1)
9-9
: LGTM! Clean integration of bank sync navigation.The changes follow the existing patterns and are well-integrated into the sidebar navigation.
Also applies to: 30-30, 67-72
packages/loot-core/src/types/prefs.d.ts (1)
31-33
: LGTM! The new preference types follow established patterns.The new string literal types for sync-related preferences maintain consistency with existing naming conventions and provide granular control over bank sync features.
Let's verify the naming consistency across the codebase:
✅ Verification successful
Naming consistency verified across the codebase
The preference key patterns are consistently implemented in both server and client code, maintaining the established naming conventions throughout the application.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for similar preference key patterns rg "custom-sync|sync-import" --type tsLength of output: 1130
packages/loot-core/src/mocks/index.ts (2)
43-43
: LGTM! Timestamp stored as string follows schema design.The implementation correctly stores the
last_sync
timestamp as a string, which aligns with the database schema design.
54-62
: LGTM! Empty sync fields are properly initialized.The
emptySyncFields
function correctly initializes all sync-related fields to null, maintaining consistency with the database schema.packages/desktop-client/src/components/banksync/FieldMapping.tsx (1)
141-149
: Add error handling for missing field mappings.The example value lookup could fail silently if the mapping is not found. Consider adding error handling or a fallback value.
const example = field.syncFields.find( f => f.field === mapping.get(field.actualField), )?.example ?? t('No example available');packages/desktop-client/src/components/banksync/EditSyncAccount.tsx (2)
1-25
: LGTM! Well-structured type definitions and imports.The type definitions and imports are well-organized, following TypeScript best practices.
124-133
: LGTM! Proper error handling for JSON parsing.The JSON parsing is properly wrapped in a try-catch block, following the previous review comment's suggestion.
packages/loot-core/src/client/state-types/modals.d.ts (1)
189-193
: LGTM! Well-typed modal definition.The new modal type is properly typed with the required
account
property.packages/desktop-client/src/components/FinancesApp.tsx (1)
27-27
: LGTM! Proper route integration.The bank sync route is properly integrated into the existing routing structure.
Also applies to: 252-252
packages/desktop-client/src/components/Modals.tsx (1)
14-14
: LGTM! Modal integration looks good.The new
EditSyncAccount
modal is properly integrated into the modal system, following the established patterns for modal rendering and prop passing.Also applies to: 388-390
packages/loot-core/src/types/models/bank-sync.d.ts (1)
22-23
: LGTM! Type definition is clear and follows conventions.The
BankSyncProviders
type appropriately defines the supported providers using a union type.packages/desktop-client/src/components/common/Input.tsx (1)
57-59
: LGTM! Good UX improvement.The conditional styling for disabled state provides better visual feedback to users.
packages/loot-core/src/server/accounts/sync.ts (3)
329-345
: Good use of Promise.all for parallel queries.The code efficiently fetches multiple preferences in parallel, improving performance.
399-400
: Good practice: Storing raw sync data.Storing the original transaction data enables better debugging and future data recovery if needed.
347-349
: Verify mapping string parsing.Ensure that
mappingsFromString
properly handles invalid mapping strings to prevent runtime errors.✅ Verification successful
✓ Mapping string parsing is properly implemented with error handling
The
mappingsFromString
function incustom-sync-mapping.ts
includes comprehensive error handling for invalid inputs:
- JSON parsing errors
- Type validation
- Null checks
- Descriptive error messages
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for error handling in mappingsFromString ast-grep --pattern 'function mappingsFromString($_) { $$$ try { $$$ } catch { $$$ } }'Length of output: 104
Script:
#!/bin/bash # Find mappingsFromString function definition ast-grep --pattern 'mappingsFromString = $_' # Also try const/let/var declaration ast-grep --pattern 'const mappingsFromString = $_' # Search for the function name in the codebase rg "mappingsFromString" -A 5Length of output: 10817
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
/update-vrt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🔭 Outside diff range comments (2)
packages/loot-core/src/server/main.ts (1)
Line range hint
1048-1066
: Add TypeScript types and improve error handling.The function implementation could be improved in several areas:
- Add TypeScript types for parameters to improve type safety
- Add error handling for the database update
- Consider storing the timestamp as an INTEGER in the database for better sorting and querying
- Consider returning the modified arrays instead of mutating external state
-async function handleSyncResponse( - res, - acct, - newTransactions, - matchedTransactions, - updatedAccounts, -) { +async function handleSyncResponse( + res: { added: Transaction[]; updated: Transaction[] }, + acct: Account, + newTransactions: Transaction[], + matchedTransactions: Transaction[], + updatedAccounts: string[], +): Promise<void> { const { added, updated } = res; newTransactions.push(...added); matchedTransactions.push(...updated); if (added.length > 0) { updatedAccounts.push(acct.id); } const ts = new Date().getTime().toString(); const id = acct.id; - await db.runQuery(`UPDATE accounts SET last_sync = ? WHERE id = ?`, [ts, id]); + try { + await db.runQuery( + `UPDATE accounts SET last_sync = ? WHERE id = ?`, + [ts, id] + ); + } catch (error) { + logger.error('Failed to update last_sync timestamp:', error); + throw new Error('Failed to update sync timestamp'); + } }packages/desktop-client/src/components/banksync/EditSyncAccount.tsx (1)
Line range hint
394-395
: Add null check for notes trimming.The
notes.trim()
call could throw ifnotes
is null or undefined.Apply this diff to add proper null checking:
- notes: importNotes ? notes.trim().replace('#', '##') : null, + notes: importNotes && notes ? notes.trim().replace('#', '##') : null,
♻️ Duplicate comments (2)
packages/desktop-client/src/components/banksync/EditSyncAccount.tsx (1)
173-179
:⚠️ Potential issueRemove unsafe non-null assertion.
The non-null assertion on
mapping!
could lead to runtime errors ifmapping
is undefined.Apply this diff to improve type safety:
- mapping={mapping!} + mapping={mapping ?? new Map()}packages/loot-core/src/server/accounts/sync.ts (1)
361-366
:⚠️ Potential issueAdd validation for mapped fields.
The code should validate that the mapped fields exist in the transaction object before accessing them.
Apply this diff to add proper validation:
- const mapping = mappings.get(trans.amount <= 0 ? 'payment' : 'deposit'); - - const date = trans[mapping.get('date')] ?? trans.date; - const payeeName = trans[mapping.get('payee')]; - const notes = trans[mapping.get('notes')]; + const direction = trans.amount <= 0 ? 'payment' : 'deposit'; + const mapping = mappings.get(direction); + + if (!mapping) { + throw new Error(`No mapping found for direction: ${direction}`); + } + + const dateField = mapping.get('date'); + const payeeField = mapping.get('payee'); + const notesField = mapping.get('notes'); + + if (!dateField || !payeeField) { + throw new Error('Required mapping fields (date, payee) are missing'); + } + + const date = trans[dateField] ?? trans.date; + const payeeName = trans[payeeField]; + const notes = notesField ? trans[notesField] : null;
🧹 Nitpick comments (9)
packages/desktop-client/e2e/page-models/bank-sync-page.js (1)
1-10
: Consider converting to TypeScript for better type safety.The page model would benefit from TypeScript conversion to match other files in the codebase and provide better type safety for the
page
andoptions
parameters.-export class BankSyncPage { +export class BankSyncPage { + private page: Page; + private bankSync: Locator; + constructor(page) { this.page = page; this.bankSync = page.getByTestId('banksync'); } - async waitFor(options) { + async waitFor(options?: WaitForOptions): Promise<void> { await this.bankSync.waitFor(options); } }packages/desktop-client/e2e/page-models/navigation.js (1)
59-70
: Extract duplicated menu expansion logic.The menu expansion logic is duplicated across multiple navigation methods. Consider extracting it into a reusable helper method.
+ private async expandMoreMenuIfNeeded(link: Locator): Promise<void> { + if (!(await link.isVisible())) { + await this.page.getByRole('button', { name: 'More' }).click(); + } + } async goToBankSyncPage() { const bankSyncLink = this.page.getByRole('link', { name: 'Bank Sync' }); - if (!(await bankSyncLink.isVisible())) { - await this.page.getByRole('button', { name: 'More' }).click(); - } + await this.expandMoreMenuIfNeeded(bankSyncLink); await bankSyncLink.click(); return new BankSyncPage(this.page); }packages/desktop-client/src/components/banksync/AccountRow.tsx (4)
12-17
: Move timestamp formatting to a shared utility.The
tsToString
function could be useful in other components. Consider moving it to a shared utility file.// New file: src/utils/dateFormatting.ts +import { format } from 'loot-core/src/shared/months'; + +export const formatTimestamp = (ts: string | null, dateFormat: string): string => { + if (!ts) return 'Unknown'; + const parsed = new Date(parseInt(ts, 10)); + return `${format(parsed, dateFormat)} ${format(parsed, 'HH:mm:ss')}`; +};
44-45
: Use optional chaining for event handlers.Replace the conditional checks with optional chaining for cleaner code.
- onMouseEnter={() => onHover && onHover(account.id)} - onMouseLeave={() => onHover && onHover(null)} + onMouseEnter={() => onHover?.(account.id)} + onMouseLeave={() => onHover?.(null)}🧰 Tools
🪛 Biome (1.9.4)
[error] 44-44: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 45-45: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
47-72
: Extract common cell styles to reduce duplication.The cell styling is duplicated across multiple cells. Consider extracting it to a constant or styled component.
+const commonCellStyle = { + color: theme.tableText, + padding: '10px', +}; <Cell name="accountName" width={250} plain - style={{ color: theme.tableText, padding: '10px' }} + style={commonCellStyle} >
74-86
: Add aria-label to improve accessibility.The Edit and Link buttons should have descriptive aria-labels for better accessibility.
- <Button onPress={() => onAction(account, 'edit')}> + <Button + onPress={() => onAction(account, 'edit')} + aria-label={`Edit ${account.name} sync settings`} + > <Trans>Edit</Trans> </Button> - <Button onPress={() => onAction(account, 'link')}> + <Button + onPress={() => onAction(account, 'link')} + aria-label={`Link ${account.name} to bank account`} + > <Trans>Link account</Trans> </Button>packages/desktop-client/src/components/banksync/FieldMapping.tsx (3)
4-5
: Consider consolidating icon imports to use a single version.Icons are imported from two different versions (
v0
andv1
). For consistency, consider using icons from the same version.
16-31
: Consider extracting transaction direction options.The transaction direction options could be defined outside the hook as a constant to improve reusability and reduce re-renders.
+const TRANSACTION_DIRECTIONS = [ + { + value: 'payment', + label: 'Payment', + }, + { + value: 'deposit', + label: 'Deposit', + }, +] as const; + const useTransactionDirectionOptions = () => { const { t } = useTranslation(); - const transactionDirectionOptions = [ - { - value: 'payment', - label: t('Payment'), - }, - { - value: 'deposit', - label: t('Deposit'), - }, - ]; + const transactionDirectionOptions = TRANSACTION_DIRECTIONS.map(({ value, label }) => ({ + value, + label: t(label), + })); return { transactionDirectionOptions }; };
85-152
: Consider memoizing mapped fields for better performance.The field mapping operation could be memoized to prevent unnecessary re-renders, especially for large datasets.
+import { useMemo } from 'react'; export function FieldMapping({ transactionDirection, setTransactionDirection, fields, mapping, setMapping, }: FieldMappingProps) { const { t } = useTranslation(); const { transactionDirectionOptions } = useTransactionDirectionOptions(); + const mappedFields = useMemo( + () => + fields.map(field => ({ + key: field.actualField, + mappedValue: mapping.get(field.actualField), + example: field.syncFields.find( + f => f.field === mapping.get(field.actualField), + )?.example, + })), + [fields, mapping], + ); return ( <> <Select /* ... */ /> <TableHeader>/* ... */</TableHeader> - {fields.map(field => { + {mappedFields.map(({ key, mappedValue, example }) => { return ( <Row - key={field.actualField} + key={key} /* ... */ > /* ... */ </Row> ); })} </> ); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (26)
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-checks-the-page-visuals-1-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-checks-the-page-visuals-2-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-checks-the-page-visuals-3-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-1-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-2-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-3-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-4-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-5-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-6-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-split-transaction-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-1-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-split-transaction-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-2-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/rules.test.ts-snapshots/Rules-creates-a-split-transaction-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-3-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-10-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-11-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-12-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-13-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-14-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/schedules.test.ts-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-15-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/settings.test.ts-snapshots/Settings-checks-the-page-visuals-1-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/settings.test.ts-snapshots/Settings-checks-the-page-visuals-2-chromium-linux.png
is excluded by!**/*.png
packages/desktop-client/e2e/settings.test.ts-snapshots/Settings-checks-the-page-visuals-3-chromium-linux.png
is excluded by!**/*.png
packages/loot-core/src/server/__snapshots__/main.test.ts.snap
is excluded by!**/*.snap
packages/loot-core/src/server/accounts/__snapshots__/parse-file.test.ts.snap
is excluded by!**/*.snap
packages/loot-core/src/server/accounts/__snapshots__/sync.test.ts.snap
is excluded by!**/*.snap
packages/loot-core/src/server/accounts/__snapshots__/transfer.test.ts.snap
is excluded by!**/*.snap
upcoming-release-notes/4253.md
is excluded by!**/*.md
📒 Files selected for processing (25)
packages/desktop-client/e2e/bank-sync.test.ts
(1 hunks)packages/desktop-client/e2e/page-models/bank-sync-page.js
(1 hunks)packages/desktop-client/e2e/page-models/navigation.js
(2 hunks)packages/desktop-client/src/components/FinancesApp.tsx
(2 hunks)packages/desktop-client/src/components/Modals.tsx
(2 hunks)packages/desktop-client/src/components/banksync/AccountRow.tsx
(1 hunks)packages/desktop-client/src/components/banksync/AccountsHeader.tsx
(1 hunks)packages/desktop-client/src/components/banksync/AccountsList.tsx
(1 hunks)packages/desktop-client/src/components/banksync/EditSyncAccount.tsx
(1 hunks)packages/desktop-client/src/components/banksync/FieldMapping.tsx
(1 hunks)packages/desktop-client/src/components/banksync/index.tsx
(1 hunks)packages/desktop-client/src/components/common/Input.tsx
(1 hunks)packages/desktop-client/src/components/sidebar/PrimaryButtons.tsx
(3 hunks)packages/loot-core/migrations/1738072673000_bank_sync_page.sql
(1 hunks)packages/loot-core/src/client/accounts/accountsSlice.ts
(1 hunks)packages/loot-core/src/client/state-types/modals.d.ts
(1 hunks)packages/loot-core/src/mocks/index.ts
(3 hunks)packages/loot-core/src/server/accounts/sync.ts
(3 hunks)packages/loot-core/src/server/aql/schema/index.ts
(2 hunks)packages/loot-core/src/server/main.ts
(4 hunks)packages/loot-core/src/server/util/custom-sync-mapping.ts
(1 hunks)packages/loot-core/src/types/models/account.d.ts
(1 hunks)packages/loot-core/src/types/models/bank-sync.d.ts
(1 hunks)packages/loot-core/src/types/models/transaction.d.ts
(1 hunks)packages/loot-core/src/types/prefs.d.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (17)
- packages/loot-core/src/types/models/bank-sync.d.ts
- packages/loot-core/src/types/models/transaction.d.ts
- packages/desktop-client/src/components/common/Input.tsx
- packages/loot-core/migrations/1738072673000_bank_sync_page.sql
- packages/desktop-client/src/components/sidebar/PrimaryButtons.tsx
- packages/desktop-client/src/components/FinancesApp.tsx
- packages/loot-core/src/client/state-types/modals.d.ts
- packages/loot-core/src/types/prefs.d.ts
- packages/loot-core/src/server/aql/schema/index.ts
- packages/loot-core/src/client/accounts/accountsSlice.ts
- packages/desktop-client/src/components/banksync/AccountsHeader.tsx
- packages/loot-core/src/types/models/account.d.ts
- packages/desktop-client/src/components/Modals.tsx
- packages/desktop-client/src/components/banksync/AccountsList.tsx
- packages/loot-core/src/server/util/custom-sync-mapping.ts
- packages/loot-core/src/mocks/index.ts
- packages/desktop-client/src/components/banksync/index.tsx
🧰 Additional context used
📓 Learnings (1)
packages/desktop-client/src/components/banksync/AccountRow.tsx (2)
Learnt from: matt-fidd
PR: actualbudget/actual#4253
File: packages/desktop-client/src/components/banksync/AccountRow.tsx:0-0
Timestamp: 2025-01-28T23:24:32.957Z
Learning: In the Actual codebase, timestamp formatting should use date-fns' `format` function with explicit format strings (e.g., 'HH:mm:ss' for time) instead of JavaScript's built-in `toLocaleTimeString()` for consistency.
Learnt from: joel-jeremy
PR: actualbudget/actual#3685
File: packages/desktop-client/src/components/accounts/Account.tsx:655-665
Timestamp: 2024-11-10T16:45:31.225Z
Learning: The Account component in 'packages/desktop-client/src/components/accounts/Account.tsx' is being rewritten in a separate PR.
🪛 Biome (1.9.4)
packages/desktop-client/src/components/banksync/AccountRow.tsx
[error] 44-44: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 45-45: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: Visual regression
- GitHub Check: Functional
- GitHub Check: build (windows-latest)
- GitHub Check: build (ubuntu-latest)
🔇 Additional comments (3)
packages/loot-core/src/server/main.ts (2)
1134-1134
: LGTM! Well-structured error handling.The usage of
handleSyncResponse
in the bank sync handler is well implemented with proper error handling and logging.
1203-1203
: LGTM! Well-structured error handling.The usage of
handleSyncResponse
in the SimpleFin batch sync handler is well implemented with proper error handling and logging.packages/desktop-client/src/components/banksync/FieldMapping.tsx (1)
33-39
: LGTM! Well-structured type definitions.The props interface is clear, properly typed, and follows TypeScript best practices.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (1)
packages/desktop-client/src/components/banksync/EditSyncAccount.tsx (1)
108-113
: Improve boolean state initialization.The string comparison for boolean state initialization could be simplified using a utility function.
+const toBool = (value: string | boolean) => String(value) === 'true'; + const [importPending, setImportPending] = useState( - String(savedImportPending) === 'true', + toBool(savedImportPending) ); const [importNotes, setImportNotes] = useState( - String(savedImportNotes) === 'true', + toBool(savedImportNotes) );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/desktop-client/src/components/banksync/EditSyncAccount.tsx
(1 hunks)packages/loot-core/src/server/util/custom-sync-mapping.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/loot-core/src/server/util/custom-sync-mapping.ts
⏰ Context from checks skipped due to timeout of 90000ms (5)
- GitHub Check: Visual regression
- GitHub Check: Functional
- GitHub Check: build (windows-latest)
- GitHub Check: build (ubuntu-latest)
- GitHub Check: Analyze
🔇 Additional comments (3)
packages/desktop-client/src/components/banksync/EditSyncAccount.tsx (3)
26-40
: LGTM! Well-structured type definitions.The type definitions are clear, properly exported, and provide good type safety for the component.
79-88
: Improve type safety in field mapping.The type assertion
syncField as keyof TransactionEntity
could lead to runtime errors ifsyncField
is not actually a key ofTransactionEntity
.
182-188
: Remove unsafe non-null assertion.The non-null assertion on
mapping!
could lead to runtime errors ifmapping
is undefined.
Server PR needs merging first: actualbudget/actual-server#558
Features
Notes
Testing
Screenshots
Potential TODOs
Issues
Closes #4138
Closes #3838
Closes #2759
Closes #1467